﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using MontagsmalerApp.Utils;
using System.Windows.Threading;
using System.Collections.Specialized;
using MontagsmalerApp.Models;

namespace MontagsmalerApp.ViewModels
{
	/// <summary>
	/// Diese Klasse repräsentiert ein Montagsmaler-Spiel.
	/// </summary>
	public class Spiel : INotifyPropertyChanged
	{
		#region Events
		/// <summary>
		/// Delegate für NeueMalrundeEvent
		/// </summary>
		public delegate void NeueMalrundeHandler();

		/// <summary>
		/// Event wird aufgerufen, wenn eine neue Malrunde eingeleitet wurde
		/// </summary>
		public event NeueMalrundeHandler NeueMalrundeEvent;

		/// <summary>
		/// Delegate für NeuerBegriffEvent
		/// </summary>
		public delegate void NeuerBegriffHandler();

		/// <summary>
		/// Event wird aufgerufen, wenn ein neuer Begriff ausgewählt wurde
		/// </summary>
		public event NeuerBegriffHandler NeuerBegriffEvent;


		/// <summary>
		/// Delegate für BegriffBeendetEvent
		/// </summary>
		public delegate void BegriffBeendetHandler(Begriff begriff, bool erraten);

		/// <summary>
		/// Event wird aufgerufen, wenn der Begriff beendet wurde
		/// </summary>
		public event BegriffBeendetHandler BegriffBeendetEvent;

		/// <summary>
		/// Delegate für MalrundeBeendetEvent
		/// </summary>
		public delegate void MalrundeBeendetHandler();

		/// <summary>
		/// Event wird aufgerufen, wenn die Malrunde beendet ist
		/// </summary>
		public event MalrundeBeendetHandler MalrundeBeendetEvent;
		#endregion

		#region VerbleibendeSekundenBegriff
		private int _VerbleibendeSekundenBegriff;
		/// <summary>
		/// Die Sekunden die das Team noch Zeit hat, um Punkte zu machen.
		/// </summary>
		public int VerbleibendeSekundenBegriff
		{
			get
			{
				return _VerbleibendeSekundenBegriff;
			}
			set
			{
				_VerbleibendeSekundenBegriff = value;
				OnPropertyChanged("VerbleibendeSekundenBegriff");
			}
		}
		#endregion

		#region VerbleibendeSekundenMalrunde
		private int _VerbleibendeSekundenMalrunde;
		/// <summary>
		/// Die Sekunden die das Team noch Zeit hat, um Punkte zu machen.
		/// </summary>
		public int VerbleibendeSekundenMalrunde
		{
			get
			{
				return _VerbleibendeSekundenMalrunde;
			}
			set
			{
				_VerbleibendeSekundenMalrunde = value;
				OnPropertyChanged("VerbleibendeSekundenMalrunde");
			}
		}
		#endregion

		#region MaximaleSekundenBegriff
		private int _MaximaleSekundenBegriff;
		/// <summary>
		/// Die Sekunden die der Malende noch Zeit hat, um den Begriff zu zeichnen.
		/// </summary>
		public int MaximaleSekundenBegriff
		{
			get
			{
				return _MaximaleSekundenBegriff;
			}
		}
		#endregion

		#region MaximaleSekundenMalrunde
		private int _MaximaleSekundenMalrunde;
		/// <summary>
		/// Die Sekunden die der Malende noch Zeit hat, um den Begriff zu zeichnen.
		/// </summary>
		public int MaximaleSekundenMalrunde
		{
			get
			{
				return _MaximaleSekundenMalrunde;
			}
		}
		#endregion

		#region Begriffsammlung
		/// <summary>
		/// Liste mit allen möglichen Begriffen.
		/// </summary>
		public Begriffsammlung Begriffsammlung { get; set; }
		#endregion

		#region NichtBenutzteBegriffe
		/// <summary>
		/// Alle Begriffe, die in diesem Spiel noch nicht dran waren.
		/// </summary>
		public IList<Begriff> NichtBenutzteBegriffe { get; set; }
		#endregion

		#region Spielerverwaltung
		/// <summary>
		/// Die Spielerverwaltung.
		/// </summary>
		public Spielerverwaltung Spielerverwaltung { get; set; }
		#endregion

		#region AktuellerBegriff
		private Begriff _AktuellerBegriff;
		/// <summary>
		/// Der aktuelle Begriff, der gemalt werden soll.
		/// </summary>
		public Begriff AktuellerBegriff
		{
			get
			{
				return _AktuellerBegriff;
			}
			set
			{
				_AktuellerBegriff = value;
				OnPropertyChanged("AktuellerBegriff");
			}
		}
		#endregion

		#region RichtigeAntworten
		private int _RichtigeAntworten;
		/// <summary>
		/// Anzahl der richtig erratenen Begriffe.
		/// </summary>
		public int RichtigeAntworten
		{
			get
			{
				return _RichtigeAntworten;
			}
			set
			{
				_RichtigeAntworten = value;
				OnPropertyChanged("RichtigeAntworten");
			}
		}
		#endregion

		#region FalscheAntworten
		private int _FalscheAntworten;
		/// <summary>
		/// Anzahl der nicht erratenen Begriffe.
		/// </summary>
		public int FalscheAntworten
		{
			get
			{
				return _FalscheAntworten;
			}
			set
			{
				_FalscheAntworten = value;
				OnPropertyChanged("FalscheAntworten");
			}
		}
		#endregion

		#region SpielLaeuftGerade
		private bool _SpielLaeuftGerade;
		/// <summary>
		/// Gibt wieder, ob das Spiel im Moment läuft oder nicht.
		/// </summary>
		public bool SpielLaeuftGerade
		{
			get
			{
				return _SpielLaeuftGerade;
			}
			set
			{
				_SpielLaeuftGerade = value;
				OnPropertyChanged("SpielLaeuftGerade");
				OnPropertyChanged("SpielKannGestartetWerden");
				OnPropertyChanged("MalrundeKannGestartetWerden");
			}
		}
		#endregion

		#region SpielKannGestartetWerden
		/// <summary>
		/// True, wenn alle Voraussetzungen gegeben sind, um ein Spiel zu starten.
		/// </summary>
		public bool SpielKannGestartetWerden
		{
			get
			{
				return !SpielLaeuftGerade;
			}
		}
		#endregion

		#region MalrundeLaeuftGerade
		private bool _MalrundeLaeuftGerade;
		/// <summary>
		/// Gibt wieder, ob momentan jemand eine Malrunde macht.
		/// </summary>
		public bool MalrundeLaeuftGerade
		{
			get
			{
				return _MalrundeLaeuftGerade;
			}
			set
			{
				_MalrundeLaeuftGerade = value;
				OnPropertyChanged("MalrundeLaeuftGerade");
				OnPropertyChanged("MalrundeKannGestartetWerden");
			}
		}
		#endregion

		#region MalrundeKannGestartetWerden
		/// <summary>
		/// True, wenn alle Voraussetzungen gegeben sind, um eine Malrunde zu starten.
		/// </summary>
		public bool MalrundeKannGestartetWerden
		{
			get
			{
				return !MalrundeLaeuftGerade && SpielLaeuftGerade && AktuellerSpieler != null;
			}
		}
		#endregion

		#region AktuellerSpieler
		/// <summary>
		/// Der Spieler, der gerade malt bzw. als letztes gemalt hat.
		/// </summary>
		public Spielteilnehmer AktuellerSpieler
		{
			get
			{
				return Spielerverwaltung.MalenderSpieler;
			}
			set
			{
        Spielerverwaltung.MalenderSpieler = value;

        if (Spielerverwaltung.MalenderSpieler != null)
        {
          Status.MalrundeVorbereiten();

          if (NeueMalrundeEvent != null)
            NeueMalrundeEvent();
        }

        OnPropertyChanged("AktuellerSpieler");
				OnPropertyChanged("MalrundeKannGestartetWerden");
			}
		}
		#endregion

		#region Meldung
		private string _Meldung;
		/// <summary>
		/// Eine aktuelle Meldung, der Informationen zum Zustand des Spiels gibt.
		/// </summary>
		public string Meldung
		{
			get
			{
				return _Meldung;
			}
			set
			{
				_Meldung = value;
				OnPropertyChanged("Meldung");
			}
		}
		#endregion

		#region Status
		/// <summary>
		/// Der Status des Spiels.
		/// </summary>
		public SpielStatus Status { get; set; }
		#endregion

		/// <summary>
		/// Initialisiert die Spielklasse und liefert eine neue Instanz zurück, die bereits vorbereitet ist.
		/// </summary>
		public Spiel()
		{
			Spielerverwaltung = Spielerverwaltung.Instance;
			_MaximaleSekundenBegriff = ConfigurationParameters.GetTimerBegriffFromConfig();
			_MaximaleSekundenMalrunde = ConfigurationParameters.GetTimerSpielrundeFromConfig();

			Status = new SpielUnvorbereitet(this);
		}

		/// <summary>
		/// Bereitet alle Properties auf ein neues Spiel vor.
		/// </summary>
		public void SpielVorbereiten()
		{
			Status.SpielVorbereiten();
		}

		/// <summary>
		/// Startet ein neues Spiel. Alle Begriffe sind dann wieder verfügbar.
		/// </summary>
		/// <param name="random"></param>
		public void NeuesSpiel()
		{
			Status.MalrundeVorbereiten();
		}

		/// <summary>
		/// Startet eine neue Malrunde mit dem aktuellen Spieler.
		/// </summary>
		public void NeueMalrunde()
		{
			Status.NeuenBegriffErzeugen();

			if (NeuerBegriffEvent != null)
				NeuerBegriffEvent();
		}

		/// <summary>
		/// Schreibt die Punkte gut.
		/// </summary>
		public void MalrundeBeenden()
		{
			Status.MalrundeBeenden();

			if (MalrundeBeendetEvent != null)
				MalrundeBeendetEvent();
		}

		/// <summary>
		/// Der Spieler erhält Punkte gutgeschrieben und ein neuer Begriff wird ausgesucht.
		/// </summary>
		public void BegriffWurdeErraten()
		{
			BegriffBeenden(true);

			Status.NeuenBegriffErzeugen();
			if (NeuerBegriffEvent != null)
				NeuerBegriffEvent();
		}

		/// <summary>
		/// Der Spieler erhält keine Punkte gutgeschrieben und es wird ein neuer Begriff ausgesucht.
		/// </summary>
		public void BegriffWurdeNichtErraten()
		{
			BegriffBeenden(false);

			Status.NeuenBegriffErzeugen();
			if (NeuerBegriffEvent != null)
				NeuerBegriffEvent();
		}

		/// <summary>
		/// Beendet den Begriff und schreibt Punkte gut.
		/// </summary>
		/// <param name="erraten"></param>
		public void BegriffBeenden(bool erraten)
		{
      // Aktuellen Begriff dem Event übergeben
      Begriff aktuellerBegriff = this.AktuellerBegriff;

      Status.BegriffBeenden(erraten);
			if (BegriffBeendetEvent != null)
        BegriffBeendetEvent(aktuellerBegriff, erraten);
		}

		/// <summary>
		/// Beendet das aktuelle Spiel.
		/// </summary>
		public void SpielBeenden()
		{
			Status.SpielBeenden();
		}

		/// <summary>
		/// Speichert die aktuellen Teams unter dem angegebenen Pfad. Damit kann man sich eine Sicherheitskopie der Teams anlegen.
		/// </summary>
		/// <param name="pfad"></param>
		public void SpeichereTeams(string pfad)
		{
			Spielerverwaltung.Speichern(pfad);
		}

		/// <summary>
		/// Speichert die aktuellen Teams in einer internen Speicherdatei.
		/// </summary>
		public void SpeichereTeams()
		{
			Spielerverwaltung.Speichern();
		}

		/// <summary>
		/// Lädt die Intanz der Spielerverwaltung neu.
		/// </summary>
		/// <param name="pfad"></param>
		public void LadeTeams(string pfad)
		{
			Spielerverwaltung.Laden(pfad);
		}

		/// <summary>
		/// Lädt die Intanz der Spielerverwaltung neu.
		/// </summary>
		public void LadeTeams()
		{
			Spielerverwaltung.Laden();
		}

		/// <summary>
		/// Aktualisiert die Spiel-Meldung.
		/// </summary>
		/// <param name="text"></param>
		public void Log(string text)
		{
			Meldung = text;
		}

		#region Timer
		/// <summary>
		/// Der Timer.
		/// </summary>
		private DispatcherTimer countdownTimer;

		/// <summary>
		/// Timer starten.
		/// </summary>
		public void StartTimer()
		{
			if (countdownTimer == null)
			{
				countdownTimer = new DispatcherTimer();
				countdownTimer.Interval = new TimeSpan(0, 0, 1);
				countdownTimer.Tick += new EventHandler(countdownTimer_Tick);
			}

			VerbleibendeSekundenBegriff = MaximaleSekundenBegriff;
			VerbleibendeSekundenMalrunde = MaximaleSekundenMalrunde;

			countdownTimer.Start();
		}

		/// <summary>
		/// Timer anhalten.
		/// </summary>
		public void EndTimer()
		{
			if (countdownTimer != null)
				countdownTimer.Stop();
		}

		/// <summary>
		/// Reagiere, wenn eine Sekunde vergangen ist.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void countdownTimer_Tick(object sender, EventArgs e)
		{
			// Gesamte Zeit abgelaufen
			if (VerbleibendeSekundenMalrunde < 1)
			{
				MalrundeBeenden();
				return;
			}

			// Begriff-Zeit abgelaufen
			if (VerbleibendeSekundenBegriff < 1)
			{
				BegriffWurdeNichtErraten();
				VerbleibendeSekundenBegriff = MaximaleSekundenBegriff;
			}

			// Sekunden runterzählen
			VerbleibendeSekundenBegriff--;
			VerbleibendeSekundenMalrunde--;
		}
		#endregion

		#region PropertyChanged
		/// <summary>
		/// Der Eventhandler, der der Oberfläche Änderungen mitteilt.
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;

		/// <summary>
		/// Sagt Bescheid, wenn sich ein Property geändert hat.
		/// </summary>
		/// <param name="propertyName">Name der Property.</param>
		protected void OnPropertyChanged(string propertyName)
		{
			PropertyChangedEventHandler handler = PropertyChanged;

			if (handler != null)
			{
				handler(this, new PropertyChangedEventArgs(propertyName));
			}
		}
 	 #endregion

		/// <summary>
		/// Liefert die aktuellen Spielstände. Welches Team wird wohl der Gewinner sein?
		/// </summary>
		/// <returns></returns>
		public List<Platzierung> GetPlatzierungen()
		{
			return Spielerverwaltung.GetPlatzierungen();
		}
	}
}
